package com.example.slogantv.utlis;


import android.util.Base64;


import com.aliyun.vod.common.utils.IOUtils;
import com.aliyun.vod.common.utils.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;


public class SignUtils {

    //签名算法HmacSha256
    public static final String HMAC_SHA256 = "HmacSHA256";
    //编码
    private static final String ENCODING = "UTF-8";
    private static String secret="F5VUDjxY&atPEYpllWOV@ge0aDzawOU*26j1QaLYdCW$2@wPvx$%2LOmlM4c7ArsK2aD7L!xFSLBLSvHgYT78$";
    static List<String> signHeaders = new ArrayList<>(Arrays.asList("clientType","version","appId","timestamp"));

    /**
     * 计算HTTP请求签名
     *
     * @param uri              原始HTTP请求PATH（不包含Query）
     * @param httpMethod       原始HTTP请求方法
     * @param headers          原始HTTP请求所有请求头
     * @param paramsMap        原始HTTP请求所有Query+Form参数
     * @param inputStreamBytes 原始HTTP请求Body体（仅当请求为POST/PUT且非表单请求才需要设置此属性,表单形式的需要将参数放到paramsMap中）
     * @return 签名结果
     * @throws Exception
     */
    public static String serviceSign(String uri, String httpMethod, Map<String, String> headers, Map<Object, Object> paramsMap, byte[] inputStreamBytes) {
        try {
        Map<String, String> headersToSign = buildHeadersToSign(headers);
        String bodyMd5 = null;
        bodyMd5 = buildBodyMd5(httpMethod, inputStreamBytes);
        String resourceToSign = buildResource(uri, paramsMap);
        String stringToSign = buildStringToSign(headersToSign, resourceToSign, httpMethod, bodyMd5);
        System.out.println("签名前："+stringToSign);
        Mac hmacSha256 = Mac.getInstance(HMAC_SHA256);

        byte[] keyBytes = secret.getBytes(ENCODING);
        hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, HMAC_SHA256));
        return new String(Base64.encode(hmacSha256.doFinal(stringToSign.getBytes(ENCODING)), Base64.DEFAULT), ENCODING);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 构建BodyMd5
     *
     * @param httpMethod       HTTP请求方法
     * @param inputStreamBytes HTTP请求Body体字节数组
     * @return Body Md5值
     * @throws IOException
     */
    private static String buildBodyMd5(String httpMethod, byte[] inputStreamBytes) throws IOException {
        if (inputStreamBytes == null) {
            return null;
        }
//        if (!httpMethod.equalsIgnoreCase(HTTP_METHOD_POST) && !httpMethod.equalsIgnoreCase(HTTP_METHOD_PUT)) {
//            return null;
//        }
        InputStream inputStream = new ByteArrayInputStream(inputStreamBytes);
        byte[] bodyBytes = IOUtils.toByteArray(inputStream);
        if (bodyBytes != null && bodyBytes.length > 0) {
            return base64AndMD5(bodyBytes).trim();
        }
        return null;
    }

    /**
     * 将Map转换为用&及=拼接的字符串
     */
    private static String buildMapToSign(Map<Object, Object> paramMap) {
        StringBuilder builder = new StringBuilder();

        for (Map.Entry<Object, Object> e : paramMap.entrySet()) {
            if (builder.length() > 0) {
                builder.append('&');
            }

            String key = (String) e.getKey();
            Object value = e.getValue();

            if (value != null) {
                if (value instanceof List) {
                    List list = (List) value;
                    if (list.size() == 0) {
                        builder.append(key);
                    } else {
                        builder.append(key).append("=").append(String.valueOf(list.get(0)));
                    }
                } else if (value instanceof Object[]) {
                    Object[] objs = (Object[]) value;
                    if (objs.length == 0) {
                        builder.append(key);
                    } else {
                        builder.append(key).append("=").append(String.valueOf(objs[0]));
                    }
                } else {
                    builder.append(key).append("=").append(String.valueOf(value));
                }
            }
        }

        return builder.toString();
    }

    /**
     * 构建参与签名的HTTP头
     * <pre>
     * 传入的Headers必须将默认的ISO-8859-1转换为UTF-8以支持中文
     * </pre>
     *
     * @param headers HTTP请求头
     * @return 所有参与签名计算的HTTP请求头
     */
    private static Map<String, String> buildHeadersToSign(Map<String, String> headers) {
        Map<String, String> headersToSignMap = new TreeMap<String, String>();

        if (signHeaders != null && signHeaders.size()>0) {
            for (String headerKey : signHeaders) {
                headersToSignMap.put(headerKey, !StringUtils.isEmpty(headers.get(headerKey))?headers.get(headerKey):headers.get(headerKey.toLowerCase()));
            }
        }

        return headersToSignMap;
    }

    /**
     * 组织待计算签名字符串
     *
     * @param headers        HTTP请求头
     * @param resourceToSign Uri+请求参数的签名字符串
     * @param method         HTTP方法
     * @param bodyMd5        Body Md5值
     * @return 待计算签名字符串
     */
    private static String buildStringToSign(Map<String, String> headers, String resourceToSign, String method, String bodyMd5) {
        StringBuilder sb = new StringBuilder();
        sb.append(method);
        if (!StringUtils.isEmpty(bodyMd5)) {
            sb.append(bodyMd5);
        }
        sb.append(buildHeaders(headers));
        sb.append(resourceToSign);
        return sb.toString();
    }

    /**
     * 组织Headers签名签名字符串
     *
     * @param headers HTTP请求头
     * @return Headers签名签名字符串
     */
    private static String buildHeaders(Map<String, String> headers) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> e : headers.entrySet()) {
            if (e.getValue() != null) {
                sb.append(e.getKey()).append(':').append(e.getValue());
            }
        }
        return sb.toString();
    }

    /**
     * 组织Uri+请求参数的签名字符串
     *
     * @param uri       HTTP请求uri,不包含Query
     * @param paramsMap HTTP请求所有参数（Query+Form参数）
     * @return Uri+请求参数的签名字符串
     */
    private static String buildResource(String uri, Map<Object, Object> paramsMap) {
        StringBuilder builder = new StringBuilder();

        // uri
        builder.append(uri);

        // Query+Form
        TreeMap<Object, Object> sortMap = new TreeMap<Object, Object>();
        sortMap.putAll(paramsMap);


        // 有Query+Form参数
        if (sortMap.size() > 0) {
            builder.append('?');
            builder.append(buildMapToSign(sortMap));
        }

        return builder.toString();
    }

    /**
     * 先进行MD5摘要再进行Base64编码获取摘要字符串
     *
     * @param bytes 待计算字节数组
     * @return
     */
    public static String base64AndMD5(byte[] bytes) {
        if (bytes == null) {
            throw new IllegalArgumentException("bytes can not be null");
        }

        try {
            final MessageDigest md = MessageDigest.getInstance("MD5");
            md.reset();
            md.update(bytes);
            return new String(Base64.encode(md.digest(), Base64.DEFAULT));
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("unknown algorithm MD5");
        }
    }
}
